<template>
    <div class="chart trade-view-comp cont-box">
        <div id="trade-view-root" style="width:390px;height:415px"></div>
        <div class="loading-box" v-if="chartLoading">
            <van-loading type="spinner"></van-loading>
        </div>
    </div>
</template>

<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { init, dispose } from 'klinecharts'
import { useMit } from '@/hooks/useEventBus'
import config from './config'
import { inflate } from 'pako';
const WS_URL = import.meta.env.VITE_APP_WS;

let chart = null
const props = defineProps(['type'])
const emit = defineEmits(['fromRandomCurrentPrice'])




const chartLoading = ref(true)
const symbol = ref('')
function tradingViewInit(code, interval) {
    symbol.value = code
    chart = init('trade-view-root', config)
    chart.setOffsetRightSpace(0)
    chart.setDataSpace(10)
    chart.createTechnicalIndicator('MA', false, { id: 'candle_pane' });
    chart.createTechnicalIndicator('VOL');
    chart.createTechnicalIndicator('MACD');
    // chart.setPriceVolumePrecision(6, 5)
    fetchData(interval)
}

const interval = ref('')
async function fetchData(Interval) {
    interval.value = Interval
    if (Interval == 'deep') {
        Interval = '1'
    }
    chartLoading.value = true
    let to = new Date().getTime()
    let from = getInterval.value
    let res = await reqKlinHistory(
        symbol.value,
        Interval,
        from / 1000,
        to / 1000
    );
    chartLoading.value = false
    let bars = []
    let meta = { noData: false }
    if (res.data.length < 1) {
        meta.noData = true;
        meta.nextTime = res.nextTime; // 如果没有数据，应该有nextTime
    } else {
        // console.log(JSON.parse(res.data[res.data.length - 1]).time);
        // console.log(res.data);
        res.data.forEach((e) => {
            let obj = JSON.parse(e);
            const { high, low } = obj;
            // console.log( high, low, obj.open, obj.close);

            if (high && low) {
                bars.push({
                    open: obj.open,
                    close: obj.close,
                    high: obj.high,
                    low: obj.low,
                    volume: Number(obj.amount),
                    turnover: obj.volume || obj.vol,
                    timestamp: obj.time - 0,
                });
            }
        });

    }
    setChartType()
    chart.applyNewData(bars);
    subscribeBars(Interval)
}

const getInterval = computed(() => {
    let Interval = interval.value
    if (Interval == 'deep') {
        Interval = '1'
    }
    let to = new Date().getTime()
    const day = 1000 * 60 * 60 * 24
    let from = NaN
    switch (Interval) {
        case '1': { // 1分钟获取1周
            let minus = day * 7 // 1周
            from = to - minus
        }
            break;
        case '5': { // 5分钟获取1周
            let minus = day * 7 // 1周
            from = to - minus
        }
            break;
        case '15': { // 15分钟获取2周
            let minus = day * 15 // 2周
            from = to - minus
        }
            break;
        case '30': { // 60分钟获取2周
            let minus = day * 14 // 2周
            from = to - minus
        }
            break;
        case '60': { // 60分钟获取2周
            let minus = day * 14 // 2周
            from = to - minus
        }
            break;
        case 'D': { // 1D获取3月
            let minus = day * 90 // 3月
            from = to - minus
        }
            break;
        case 'W': { // 1W获取1年
            let minus = day * 365 // 1年
            from = to - minus
        }
            break;
        default:
            break;
    }
    return from
})
function subscribeBars(Interval) {
    let defaultDelta = NaN
    switch (Interval) {
        case '1': // 1分钟
            defaultDelta = 1

            break;
        case '5': // 5分钟
            defaultDelta = 5

            break;
        case '15': // 15分钟
            defaultDelta = 15

            break;
        case '30': // 60分钟
            defaultDelta = 30

            break;
        case '60': // 60分钟
            defaultDelta = 60

            break;
        case 'D': // 1D
            defaultDelta = 1440

            break;
        case 'W': // 1W
            defaultDelta = 1440 * 7

            break;
        default:
            break;
    }
    useMit.on('subscribeBar', (klines) => {
        let { time, open, close, low, high, volume } = klines.data;
        // console.log(klines.data);
        // console.log({ time, open, close, low, high, volume, chart }, new Date(Number(time)));
        // console.log(volume, { volume: volume == Datafeed.lastVolume });
        const oneKline = {
            open: Number(open),
            close: Number(close),
            high: Number(high),
            low: Number(low),
            volume: Number(volume),
            timestamp: Number(time),
        }
        // 检查新数据的时间戳是否与最后一根蜡烛的时间戳相同
        const datalist = chart.getDataList()
        const lastdata = datalist[datalist.length - 1]
        let lastTime = lastdata.timestamp
        let delta = ((time - lastTime) / 1000) / 60 // 分钟

        const bool = defaultDelta - delta >= 0
        // console.log({ length: datalist.length - 1, defaultDelta, bool, delta, time, num: defaultDelta - delta });
        if (datalist.length > 0 && bool) {
            // 如果相同，更新最后一根蜡烛的数据
            oneKline.timestamp = lastTime
        } else {
            chart.updateData(oneKline);
        }
        // 将更新后的bars数组应用到图表中
        chart.updateData(oneKline);
    })
}
function setTime(interval) {
    useMit.off("subscribeBar")
    fetchData(interval)
}

function setChartType() {
    let type = 'area'

    if (interval.value == '1') {
        type = 'area'
        chart.removeTechnicalIndicator('candle_pane', 'MA')
    } else {
        type = 'candle_solid'
        chart.createTechnicalIndicator('MA', false, { id: 'candle_pane' });
    }
    chart.setStyleOptions({
        candle: {
            type
        }
    })
}




// wsBus
const promiseBreaker = () => {
    let resolve, reject;
    let promise = new Promise((s, j) => {
        // eslint-disable-line
        resolve = s;
        reject = j;
        setTimeout(reject, 15 * 1000, "pending time up");
    });
    return Object.assign(promise, { resolve, reject });
};
function unzip(b64Data) {
    let strData = atob(b64Data);
    const charData = strData.split('').map(function (x) {
        return x.charCodeAt(0);
    })
    const binData = new Uint8Array(charData);
    const data = inflate(binData);
    const array = new Uint8Array(data);
    let res = '';
    const chunk = 8 * 1024
    var i;
    for (i = 0; i < array.length / chunk; i++) {
        res += String.fromCharCode.apply(null, array.slice(i * chunk, (i + 1) * chunk));
    }
    res += String.fromCharCode.apply(null, array.slice(i * chunk));

    strData = res
    return strData;
}

const connectReady = ref(promiseBreaker())
const wsRef = ref(null)
// init
function initWs(params) {
    let ws = (wsRef.value = new WebSocket(WS_URL))
    ws.onopen = () => {
        connectReady.value.resolve()
    }
    ws.onmessage = (ev) => {
        const blob = ev.data
        if (typeof blob == "string") {
            return;
        }
        const newBlob = blob.slice(0, blob.size)
        const reader = new FileReader()
        reader.readAsBinaryString(newBlob)
        reader.onload = (evt) => {
            if (evt.target.readyState == FileReader.DONE) {
                const zipdata = unzip(evt.target.result)
                // console.log('zipdata ===>',zipdata);
                // JSON 能解析  k 线数据

                const msg = JSON.parse(zipdata);
                // console.log("msg ===>", msg);

                if (msg && msg.data && msg.data.length != 0) {


                    if (msg.method && msg.method === "oneKline") {
                        // console.log("oneKline ===>", msg.data.close);
                        // 更新kline
                        useMit.emit("subscribeBar", msg);
                        const { data } = msg
                        emit("fromRandomCurrentPrice", {
                            close: data.close,
                            higPrice: data.high24,
                            lowPrice: data.low24,
                            volume24: data.volume24,
                            volume: data.volume,
                            rate: data.rate,
                        });
                    } else if (msg.method && msg.method === "initKline") {
                        // console.log("initKline ===>", msg)
                        useMit.emit("klineHistoryData", msg)
                    }

                }
            }
        }
    }
    ws.onclose = () => {
        connectReady.value = promiseBreaker()
        initWs()
    }
    ws.onerror = () => {
        initWs()
    }
}


const timer = ref(null)
// doClose
function doClose() {
    if (timer.value !== null) {
        clearInterval(timer.value)
    }
    wsRef.value.close()
}

const from = ref(0)
const period = ref('')
const firstReq = ref(true)

// 请求k线历史数据
async function reqKlinHistory(code, resolution, fromTime, toTime) {
    await connectReady.value
    from.value = from
    if (resolution > 0 && resolution < 60) {
        period.value = resolution + "min";
    } else if (resolution == 60) {
        period.value = "60min";
    } else if (resolution == "D") {
        period.value = "1day";
    } else if (resolution == "W") {
        period.value = "1week";
    }

    const res = await new Promise((resolve, reject) => {
        useMit.on('klineHistoryData', (msg) => {
            // console.log(interval.value);
            // if (firstReq.value) {
            resolve(msg)
            // firstReq.value = false
            // }
        })

        wsRef.value.send('PING')
        clearInterval(timer.value)
        timer.value = setInterval(() => {
            wsRef.value.send('PING')
        }, 5000)
        const str = `initKline-${code}-${period.value}-${fromTime * 1000}-${toTime * 1000}`
        wsRef.value.send(str)
    })
    return res
}


// 初始化
onMounted(() => {
    initWs()
})


// 销毁
onBeforeUnmount(() => {
    doClose()
    firstReq.value = true
    useMit.off('klineHistoryData')
    useMit.off('subscribeBar')
    dispose('trade-view-root')
})
// 暴露给父组件的方法变量
defineExpose({
    chartLoading,
    promiseBreaker,
    tradingViewInit,
    setTime,
})
</script>

<style lang="less" scoped>
.trade-view-comp {
    position: relative;
}

/* flex justify-center items-center text-center text-grey absolute left-0 top-0  w-full h-full z-10 mainBackground */
.loading-box {
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    color: grey;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 10;

}
</style>